﻿// Asio server session.
// Author: Jin Qing (http://blog.csdn.net/jq0123)

#include "asio_svr_session.h"

#include "asio_server.h"  // for InsertSession()
#include "client_mailbox.h"  // for CClientMailBox
#include "event_handler.h"  // for EventHandler
#include "world.h"  // for GetServer()

const char LOG_NAME[] = "CAsioSvrSession";
const uint32_t MAX_DATA_LEN = 1024 * 1024;

CAsioSvrSession::CAsioSvrSession(CAsioServer& rServer) :
	CAsioSessionBase(rServer),
	m_socket(rServer.GetIoService()),
	m_pCltMailBox(new CClientMailBox)  // unique_ptr
{
	// DeleteCltMailBox() in ~CAsioSvrSession()
	rServer.InsertSession(*this);
}

CAsioSvrSession::~CAsioSvrSession()
{
	assert(m_pCltMailBox);
	m_rServer.EraseSession(*this);
	LOG_DEBUG(Fmt("CAsioSvrSession::~CAsioSvrSession() Remove session: Id=%u, %s:%u") %
		m_pCltMailBox->GetId() % m_pCltMailBox->GetCltAddr() %
		m_pCltMailBox->GetCltPort());
}

void CAsioSvrSession::Disconnect()
{
	if (!m_bConnected)
		return;

	// Todo（Jinq): 须延时到所有发送完成，如重复登录时须发完后再断开。
	boost::system::error_code ec;
	m_socket.close(ec);
	m_bConnected = false;
	m_pCltMailBox->ClearSendQueue();
	LOG_INFO(Fmt("Disconnected. Id=%u, %s:%u") %
		m_pCltMailBox->GetId() % m_pCltMailBox->GetCltAddr() %
		m_pCltMailBox->GetCltPort());
	EventHandler().Handle("ClientDisconnected", m_pCltMailBox->GetId());
	
	//todo 玩家从cell上退出房间。。。

}


void CAsioSvrSession::Go()
{
	boost::asio::ip::tcp::endpoint endpoint = m_socket.remote_endpoint();
	std::string sCltAddr = endpoint.address().to_string();
	uint16_t uCltPort = endpoint.port();
	assert(m_pCltMailBox);
	LOG_INFO_TO("CAsioSvrSession.Go",
		Fmt("Accepted connection. Remote=%s:%u. MbId=%u.")
		% sCltAddr % uCltPort % m_pCltMailBox->GetId());
	m_pCltMailBox->SetCltEndpoint(sCltAddr, uCltPort);
	m_bConnected = true;

	using boost::asio::spawn;
	using std::bind;
	using std::placeholders::_1;
	std::shared_ptr<CAsioSvrSession> pMe = shared_from_this();
	spawn(m_strand, bind(&CAsioSvrSession::LoopRead, pMe, _1));
	spawn(m_strand, bind(&CAsioSvrSession::LoopWrite, pMe, _1));
}

void CAsioSvrSession::LoopRead(boost::asio::yield_context yield)
{
	assert(m_bConnected);
	while (ReadOne(m_socket, yield))
		;

	Disconnect();
}

void CAsioSvrSession::LoopWrite(boost::asio::yield_context yield)
{
	assert(m_pCltMailBox);
	assert(m_bConnected);
	CClientMailBox* pmb = m_pCltMailBox.get();
	while (m_bConnected)
	{
		if (pmb->IsEmpty())
		{
			WaitMs(1, yield);
			continue;
		}

		bool ok = SendRpcs(m_socket, *pmb, yield);
		if (!ok)
		{
			Disconnect();
			break;
		}
	}
	LOG_DEBUG_TO("CAsioSvrSession.LoopWrite", "End.");
}

CMailBox& CAsioSvrSession::GetMailBox()
{
	assert(m_pCltMailBox);
	return *m_pCltMailBox;
}

const CMailBox& CAsioSvrSession::GetMailBox() const
{
	assert(m_pCltMailBox);
	return *m_pCltMailBox;
}

// Todo(Jinq): Move m_cltMb, m_svrMb to Base::m_pMailbox
